/*
** Global memory allocator wrapper implementation.
** Intercepts system malloc/free calls and redirects to global shared allocator.
*/

#define lj_global_alloc_c
#define LUA_CORE

#include "lj_obj.h"
#include "lj_global_alloc.h"
#include "lj_alloc.h"
#include <dlfcn.h>
#include <stdio.h>

/* System function pointers for fallback */
static void *(*lj_system_malloc)(size_t size) = NULL;
static void (*lj_system_free)(void *ptr) = NULL;
static void *(*lj_system_realloc)(void *ptr, size_t size) = NULL;
static void *(*lj_system_calloc)(size_t nmemb, size_t size) = NULL;

/* Global allocator state */
static volatile int global_alloc_initialized = 0;
static volatile int global_alloc_lock = 0;

/* Initialize system function pointers using dlsym */
static void init_system_functions(void)
{
  if (lj_system_malloc == NULL) {
    /* Get system malloc function pointer using RTLD_NEXT to bypass our own symbols */
    lj_system_malloc = (void*(*)(size_t))dlsym(RTLD_NEXT, "malloc");
    lj_system_free = (void(*)(void*))dlsym(RTLD_NEXT, "free");
    lj_system_realloc = (void*(*)(void*, size_t))dlsym(RTLD_NEXT, "realloc");
    lj_system_calloc = (void*(*)(size_t, size_t))dlsym(RTLD_NEXT, "calloc");
    
    if (!lj_system_malloc || !lj_system_free || !lj_system_realloc || !lj_system_calloc) {
      /* If RTLD_NEXT fails, we can't fallback to our own functions as it would cause recursion */
      fprintf(stderr, "Warning: Failed to get system memory functions via dlsym\n");
    }
  }
}

/* Initialize global allocator wrapper */
LJ_FUNC void lj_global_alloc_init(void)
{
  if (!global_alloc_initialized) {
    /* Simple spinlock for thread safety */
    while (__sync_lock_test_and_set(&global_alloc_lock, 1) == 1) {
      while (global_alloc_lock == 1) {
        /* Busy wait */
      }
      if (global_alloc_initialized) {
        return; /* Another thread already initialized */
      }
    }
    
    /* Double-check after acquiring lock */
    if (!global_alloc_initialized) {
      init_system_functions();
      global_alloc_initialized = 1;
    }
    
    /* Release lock */
    __sync_lock_release(&global_alloc_lock);
  }
}

/* Global malloc replacement */
LJ_FUNC void *lj_global_malloc(size_t size)
{
  fprintf(stderr, "[DEBUG] lj_global_malloc called with size=%zu\n", size);
  
#ifdef LJ_GLOBAL_ALLOC_ENABLED
  if (!global_alloc_initialized) {
    lj_global_alloc_init();
  }
  
  void *global_allocator = get_global_shared_allocator();
  if (global_allocator) {
    /* Use global shared allocator */
    fprintf(stderr, "[DEBUG] Using global shared allocator\n");
    return lj_alloc_f_threadsafe(global_allocator, NULL, 0, size);
  }
#endif
  
  /* Fallback to system malloc */
  if (!lj_system_malloc) {
    lj_global_alloc_init();
  }
  
  if (lj_system_malloc) {
    fprintf(stderr, "[DEBUG] Using system malloc fallback\n");
    return lj_system_malloc(size);
  } else {
    /* If system malloc is not available, we have a problem */
    fprintf(stderr, "Error: System malloc not available and global allocator failed\n");
    return NULL;
  }
}

/* Global free replacement */
LJ_FUNC void lj_global_free(void *ptr)
{
#ifdef LJ_GLOBAL_ALLOC_ENABLED
  if (!global_alloc_initialized) {
    lj_global_alloc_init();
  }
  
  void *global_allocator = get_global_shared_allocator();
  if (global_allocator && ptr) {
    /* Use global shared allocator */
    lj_alloc_f_threadsafe(global_allocator, ptr, 0, 0);
    return;
  }
#endif
  
  /* Fallback to system free */
  if (!lj_system_free) {
    lj_global_alloc_init();
  }
  if (lj_system_free && ptr) {
    lj_system_free(ptr);
  }
}

/* Global realloc replacement */
LJ_FUNC void *lj_global_realloc(void *ptr, size_t size)
{
#ifdef LJ_GLOBAL_ALLOC_ENABLED
  if (!global_alloc_initialized) {
    lj_global_alloc_init();
  }
  
  void *global_allocator = get_global_shared_allocator();
  if (global_allocator) {
    /* Use global shared allocator */
    return lj_alloc_f_threadsafe(global_allocator, ptr, 0, size);
  }
#endif
  
  /* Fallback to system realloc */
  if (!lj_system_realloc) {
    lj_global_alloc_init();
  }
  return lj_system_realloc ? lj_system_realloc(ptr, size) : NULL;
}

/* Global calloc replacement */
LJ_FUNC void *lj_global_calloc(size_t nmemb, size_t size)
{
#ifdef LJ_GLOBAL_ALLOC_ENABLED
  if (!global_alloc_initialized) {
    lj_global_alloc_init();
  }
  
  void *global_allocator = get_global_shared_allocator();
  if (global_allocator) {
    /* Use global shared allocator */
    size_t total_size = nmemb * size;
    void *ptr = lj_alloc_f_threadsafe(global_allocator, NULL, 0, total_size);
    if (ptr) {
      /* Zero out the memory */
      memset(ptr, 0, total_size);
    }
    return ptr;
  }
#endif
  
  /* Fallback to system calloc */
  if (!lj_system_calloc) {
    lj_global_alloc_init();
  }
  return lj_system_calloc ? lj_system_calloc(nmemb, size) : NULL;
}

/* Export these functions as the standard malloc/free symbols */
#ifdef LJ_GLOBAL_ALLOC_ENABLED
/* Direct function definitions to replace system calls */
void *malloc(size_t size) {
  printf("🎯 [INTERCEPTED] malloc called with size=%zu\n", size);
  fflush(stdout);
  return lj_global_malloc(size);
}

void free(void *ptr) {
  lj_global_free(ptr);
}

void *realloc(void *ptr, size_t size) {
  return lj_global_realloc(ptr, size);
}

void *calloc(size_t nmemb, size_t size) {
  return lj_global_calloc(nmemb, size);
}
#endif
